function [ data ] = simpleRNN( order,data )
  % simpleRNN: Short description
  %
  % Extended description
  %
  % data {
  % param(param.inputDim,param.hiddenDim,param.outputDim,param.inputInfo,param.outputInfo)
  % rnn
  % alpha
  % inputList
  % outputList
  % result
  % }
  %

  % new
  % train while
  % run

  if strcmp(order , 'run')

    % new
    if ~isfield(data,'rnn')
      param = data.param;
      rnn = newRNN(param.inputDim,param.hiddenDim,param.outputDim,param.inputInfo,param.outputInfo);
      data.rnn = rnn;
    end

    if isfield(data,'inputList')
      rnn = data.rnn;
      if isfield(data,'outputList')
        % tain
        alpha = 0.1;
        if isfield(data,'alpha')
          alpha = data.alpha;
        end
        data.alpha = alpha;
        rnn = trainRNN(rnn,data.inputList,data.outputList,alpha );
        data.rnn = rnn;
      end

      % run
      [ outputLay2,outputLayD2,hiddenLay2,hiddenLayD2 ] = runRNN( rnn,data.inputList );
      data.result = outputLay2;

    end

    param = data.param;
    rnn = newRNN(param.inputDim,param.hiddenDim,param.outputDim,param.inputInfo,param.outputInfo);
    out = rnn;
  end

  % test
  if strcmp(order , 'test')
    rnn1 = newRNN(3,5,1,{'t1','t2','t3'},{'o1'});
    inputList = [1,2,3;4,5,6;7,8,9;10,11,12]/13;
    outputList = [1;1;0;1];
    [ outputLay1,outputLayD1,hiddenLay1,hiddenLayD1 ] = runRNN( rnn1,inputList );
    data = [outputList,outputLay1];
    rnn2 = trainRNN( rnn1,inputList,outputList,0.1 );
    for i=1:10
      for j=1:100
        rnn2 = trainRNN( rnn2,inputList,outputList,0.1 );
      end
      [ outputLay2,outputLayD2,hiddenLay2,hiddenLayD2 ] = runRNN( rnn2,inputList );
      data = [out,outputLay2];
    end
  end

end  % simpleRNN

function [ outputLay,outputLayD,hiddenLay,hiddenLayD ] = runRNN( rnn,inputList )
  % runRNN: Short description
  %
  % Extended description

  dataSize = size(inputList,1);

  wH = rnn.wH;
  wO = rnn.wO;
  inputDim=rnn.inputDim;
  hiddenDim=rnn.hiddenDim;
  outputDim=rnn.outputDim;

  hiddenLay = zeros(dataSize,hiddenDim);
  hiddenLayD = zeros(dataSize,hiddenDim);
  outputLay = zeros(dataSize,outputDim);
  outputLayD = zeros(dataSize,outputDim);

  for i=1:dataSize
    if i==1
      [hiddenLay(i,:),hiddenLayD(i,:)] = sigmoid([inputList(i,:),zeros(1,hiddenDim),1]*wH);
    else
      [hiddenLay(i,:),hiddenLayD(i,:)] = sigmoid([inputList(i,:),hiddenLay(i-1,:),1]*wH);
    end
    [outputLay(i,:),outputLayD(i,:)] = sigmoid([hiddenLay(i,:),1]*wO);
  end

end  % function

function [ rnn ] = trainRNN( rnn,inputList,outputList,alpha )
  % trainRNN: Short description
  %
  % Extended description

  dataSize = size(inputList,1);

  wH = rnn.wH;
  wO = rnn.wO;
  inputDim=rnn.inputDim;
  hiddenDim=rnn.hiddenDim;
  outputDim=rnn.outputDim;

  [ outputLay,outputLayD,hiddenLay,hiddenLayD ] = runRNN(rnn,inputList);

  % 计算wO(隐藏层->输出层)的调整值
  % outputLay:输出层的值:O(t) = sigmoid(w1*x1(t)+w2*x2(t)+..+wN*xN(t)+wB)
  % 其导数dO(t)/dwn = sigmoid'(...)*xn(t)
  outD = (outputList - outputLay).*outputLayD;
  wOUpdate = [hiddenLay';ones(1,dataSize)] * outD;

  wHUpdate = zeros(size(wH));
  % 计算隐藏层误差
  % 隐藏层误差=输出层误差传递误差+后隐藏层误差传递误差

  % 前隐藏层误差
  hiddenDTmp = zeros(1,hiddenDim);
  for i=dataSize:-1:1
    % 输出层误差传递误差
    hiddenDFromOut = outD(i,:) * wO(1:end-1,:)';
    % 当前隐藏层总误差
    currentHiddenD = (hiddenDTmp + hiddenDFromOut) .* hiddenLayD(i,:);

    % 计算wH(输入层->隐藏层)的调整值
    % hiddenLay:隐藏层的值:H(t) = sigmoid(w1*i1(t)+w2*i2(t)+..+wN*iN(t)+wh1*h1(t-1)+wh2*h2(t-1)+..+whN*hN(t-1)+wB)
    if i == 1
      xList = [inputList(i,:),zeros(1,hiddenDim),1];
    else
      xList = [inputList(i,:),hiddenLay(i-1,:),1];
    end
    wHUpdateTmp = xList' * currentHiddenD;
    wHUpdate = wHUpdateTmp + wHUpdate;

    hiddenDTmp = currentHiddenD * wH(inputDim+1:end-1,:)';
  end

  wHNew = wH + wHUpdate*alpha;
  wONew = wO + wOUpdate*alpha;

  rnn.wH = wHNew;
  rnn.wO = wONew;

end  % trainRNN

function [ rnn ] = newRNN( inputDim,hiddenDim,outputDim,inputInfo,outputInfo )
  % newRNN: 生成RNN数据结构
  %
  % wH:输入层->隐藏层:(inputDim+1+hiddenDim),hiddenDim
  % wO:隐藏层->输出层:(hiddenDim+1),outputDim
  %
  % inputInfo:参数描述用
  % outputInfo:结果描述用
  % inputDim:
  % hiddenDim:
  % outputDim:
  %
  % Extended description

  rnn = struct('inputDim',inputDim,'hiddenDim',hiddenDim,'outputDim',outputDim);
  rnn.inputInfo = inputInfo;
  rnn.outputInfo = outputInfo;

  wH = 2*rand(inputDim+1+hiddenDim,hiddenDim) - 1;
  wO = 2*rand(1+hiddenDim,outputDim) - 1;

  rnn.wH = wH;
  rnn.wO = wO;

end  % newRNN
